import { Fragment } from '@/components/Fragment';
import { Example } from '@/components/Example';

A Theme is a structured collection of design decisions that change the appearance of a UI library. An Amplify UI theme is a structured object of [design tokens](#design-tokens), [breakpoints](#breakpoints), and [overrides](#overrides). The goals of the Amplify UI theme are:

1. **Leverage platform technologies as much as possible for performance and broad support.** This means plain CSS and CSS variables. You can always fall back to writing CSS (or a pre-processer to CSS like [Sass](https://sass-lang.com/)).
1. **Use framework-specific patterns to provide an easier developer experience.** This means providing an extendable theme which generates CSS and CSS variables for your application.

## Getting started

<Fragment>
  {({ platform }) => import(`./getting-started.${platform}.mdx`)}
</Fragment>

### Theme object

The theme object is where you define tokens for color palette, font stacks, spacing, and more. By default it will extend from the `defaultTheme` Amplify UI provides.

```javascript
export const myTheme = {
  name: 'my-theme',
  tokens: {
    colors: {
      font: {
        primary: { value: 'red' },
      },
    },
  },
};
```

### CSS

You can theme Amplify UI using CSS and CSS variables if you do not want to use the theme object structure. Amplify UI components use plain CSS so styling components can be done with CSS (or a pre-processor like [Sass](https://sass-lang.com/)). All of the design tokens defined in the Amplify theme are [CSS variables](https://developer.mozilla.org/en-US/docs/Web/CSS/--*) which can be overridden:

```css
:root, [data-amplify-theme] {
  --amplify-colors-font-primary: #333;
  /* you can also use references: */
  --amplify-colors-font-secondary: var(--amplify-colors-neutral-60);
}
```

If you want more customization than the design tokens provide, you can also override the CSS for components:

```css
/* All components have a class name starting with `amplify` */
.amplify-button {
  font-size: 2rem;
  padding: 1rem 2rem;
  background: none;
  border: 2px solid black;
}

.amplify-button:hover {
  background: gray;
}
```

Or if you prefer you can use [alternative styling with a styling libraries](/guides/css-in-js)

#### Unstyled

Amplify UI components can be use unstyled if you want full control over the look-and-feel. To use the components unstyled, import them as you normally would and do not import the CSS.

```jsx
import { Button, Card } from '@aws-amplify/ui-react';

// don't import the CSS:
// import '@aws-amplify/ui-react/styles.css';

export const App = () => {
  // ...
};
```

## Theme Structure

### Design Tokens

Amplify UI uses [Design Tokens](https://www.designtokens.org/) for storing design decisions and is the primary way to theme the components. Design tokens are categorized by type under namespaces; for example, colors go under the `colors` namespace. [Stitches](https://stitches.dev/docs/tokens), [Chakra-UI](https://chakra-ui.com/docs/styled-system/theme), and [Evergreen](https://evergreen.segment.com/introduction/theming) use a similar convention for organizing their design tokens.

```typescript file=../../../../../packages/ui/src/theme/tokens/index.ts#L25-L41

```

#### References

One import thing about design tokens is they can reference other design tokens. The default theme tokens use references a lot to make a robust system where you can modify a few tokens to have a large effect. The syntax for design token references follows the draft [W3C Design Tokens Community Group specification](https://design-tokens.github.io/community-group/format/#aliases-references)

```javascript
const myTheme = {
  name: 'my-theme',
  tokens: {
    colors: {
      font: {
        // references colors.neutral.100
        // because the default theme defines that color already
        // we don't need to re-define it here
        primary: { value: '{colors.neutral.100.value}' },
      },
    },
  },
};
```

#### Component token definitions

Amplify UI follows a consistent pattern when defining tokens for a component's states and variations. This is helpful for discovering what tokens are available for theming different aspects of a component. Amplify UI uses the following pattern:

```javascript
component[modifier][_state][child];
```

A `modifier` could be a distinct style variation, like the primary or link variant for the Button component. A modifier could also be a variation based on size, such as `small`, `medium`, or `large`.

State typically refers to a change in the component due to an interaction from the user or application itself, such as hover, focus, loading or disabled. **Note**: Amplify UI prefixes states with an underscore, `_`, to help distinguish state names from modifier names.

The [Button](https://github.com/aws-amplify/amplify-ui/blob/main/packages/ui/src/theme/tokens/components/button.ts) component is a good example of a token definition that includes multiple states and modifiers and follows this pattern:

```javascript
export const button = {
  //  ... default tokens

  // states
  _hover: {},
  _focus: {},
  _loading: {},
  _disabled: {},

  // variations with states
  primary: {
    _hover: {},
    _focus: {},
    _loading: {},
    _disabled: {},
  },

  // size modifiers
  small: {},
  large: {},
};
```

Compiled, this would create the following CSS custom properties:

```css
--amplify-component-button-hover-token: value,
--amplify-component-button-focus-token: value,
--amplify-component-button-hover-loading: value,
--amplify-component-button-focus-disabled: value,
--amplify-component-button-primary-hover-token: value,
--amplify-component-button-primary-focus-token: value,
--amplify-component-button-primary-hover-loading: value,
--amplify-component-button-primary-focus-disabled: value,
--amplify-component-button-small-token: value,
--amplify-component-button-large-token: value,
```

### Fonts

Amplify UI allows custom fonts to be used in the theme. The font tokens are defined in the `fonts` namespace. You can define your primary font stack and fallback font stack values the same way you would do in a CSS `font-family` rule.

```javascript
const myTheme = {
  name: 'my-theme',
  tokens: {
    fonts: {
      default: {
        variable: { value: 'Raleway, sans-serif' },
        static: { value: 'Raleway, sans-serif' },
      },
    },
  },
};
```

### Breakpoints

Breakpoints allow you to set media query breakpoints for responsive design. You can then define breakpoint-specific token overrides or use the breakpoints for different layouts in Javascript.

```typescript file=../../../../../packages/ui/src/theme/breakpoints.ts

```

You can modify default breakpoints in your theme's `breakpoints` definition:

```javascript
const myTheme = {
  name: 'my-theme',
  breakpoints: {
    // Will be deep merged with the default theme
    // so you don't have to override all the breakpoint values
    values: {
      // default unit is 'em'
      medium: 50,
    },
  },
  //...
};
```

_Note: Unfortunately right now CSS media queries do not support CSS variables so there is no way to customize the breakpoints using only CSS._

### Overrides

An `override` is a collection of design tokens that should take precedence in certain situations, like dark mode. Overrides are built into the theme configuration, but kept separate, so that Amplify UI can use CSS for overriding parts of the theme.

```javascript
import { defaultTheme } from '@aws-amplify/ui-react';

export const theme = {
  name: 'my-theme',
  overrides: [
    {
      colorMode: 'dark',
      tokens: {
        colors: {
          neutral: {
            10: { value: defaultTheme.tokens.colors.neutral[100].value },
            20: { value: defaultTheme.tokens.colors.neutral[90].value },
            40: { value: defaultTheme.tokens.colors.neutral[80].value },
            80: { value: defaultTheme.tokens.colors.neutral[40].value },
            90: { value: defaultTheme.tokens.colors.neutral[20].value },
            100: { value: defaultTheme.tokens.colors.neutral[10].value },
          },
          black: { value: '#fff' },
          white: { value: '#000' },
        },
      },
    },
    {
      breakpoint: 'large',
      tokens: {
        space: {
          small: { value: '1rem' },
          medium: { value: '2rem' },
          large: { value: '3rem' },
        },
      },
    },
  ],
};
```

You can override design tokens in CSS by using a media query or adding extra selectors to `[data-amplify-theme="{theme.name}"]`.

```css
@media (prefers-color-scheme: dark) {
  [data-amplify-theme='my-theme'] {
    --amplify-colors-black: #fff;
    --amplify-colors-white: #fff;
  }
}

[data-amplify-theme='my-theme'].disco {
  --amplify-colors-font-primary: pink;
}
```

### Merging multiple themes

If you have multiple themes, you can extend your base theme using the `createTheme` function.

```javascript
import { createTheme, defaultTheme } from '@aws-amplify/ui';

// by default, createTheme extends the defaultTheme.
export const baseBrandTheme = createTheme({
  name: 'base-brand-theme',
  tokens: {
    colors: {
      font: {
        primary: { value: 'red' },
      },
    },
  },
});

export const otherBrandTheme = createTheme(
  {
    name: 'other-brand-theme',
    tokens: {
      colors: {
        font: {
          primary: { value: 'blue' },
        },
      },
    },
  },
  baseBrandTheme
);
// The 2nd argument is the base theme to be extended
// if it is omitted, it will use the defaultTheme
```
